package com.gitee.sparta.common.helpers;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.StreamReadFeature;
import com.fasterxml.jackson.core.json.JsonReadFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;

import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * JSON util
 *
 * @author zyred
 * @since 1.0
 */
public class JsonHelper {

    public static ObjectMapper OBJECT_MAPPER;

    static {
        OBJECT_MAPPER = JsonMapper.builder()
                // 反序列化忽略bean中不存在的字段
                .enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                // key允许无引号
                .enable(JsonReadFeature.ALLOW_UNQUOTED_FIELD_NAMES)
                // 允许出现特殊字符和转义符
                .enable(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS)
                // 允许出现单引号
                .enable(JsonReadFeature.ALLOW_SINGLE_QUOTES)
                // 允许出现数字是 0 开始的
                .enable(JsonReadFeature.ALLOW_LEADING_ZEROS_FOR_NUMBERS)
                // 允许出现数字 Nan 的写法
                .enable(JsonReadFeature.ALLOW_NON_NUMERIC_NUMBERS)
                // 关闭 jackson 允许 key 重复
                .disable(StreamReadFeature.STRICT_DUPLICATE_DETECTION)
                .build();
        // 忽略空值
        OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

    /**
     * 使用泛型方法，把json字符串转换为相应的JavaBean对象。
     * (1)转换为普通JavaBean：readValue(json,Student.class)
     * (2)转换为List,如List<Student>,将第二个参数传递为Student [].class.
     * 然后使用Arrays.asList();方法把得到的数组转换为特定类型的List
     */
    public static <T> T getObjectFromJsonString(String jsonStr, Class<T> valueType) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, valueType);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static Collection<Object> getCollection(String jsonStr, Class<? extends Collection<Object>> collectionClass, Class<?> elementClass) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, getCollectionType(collectionClass, elementClass));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static JavaType getCollectionType(Class<? extends Collection<Object>> collectionClass, Class<?> elementClass) {
        return TypeFactory.defaultInstance().constructCollectionType(collectionClass, elementClass);
    }

    public static <K, V> Map<K, V> getMap(String jsonStr, Class<? extends Map> mapClass, Class<K> keyClass, Class<V> valueClass) {
        try {
            return OBJECT_MAPPER.readValue(jsonStr, getMapType(mapClass, keyClass, valueClass));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static JavaType getMapType(Class<? extends Map> mapClass, Class<?> keyClass, Class<?> valueClass) {
        return TypeFactory.defaultInstance().constructMapType(mapClass, keyClass, valueClass);
    }

    /**
     * 把JavaBean转换为json字符串
     */
    public static String getJsonString(Object object) {
        try {
            return OBJECT_MAPPER.writeValueAsString(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据属性名获取该属性的值，如果有多个则返回 第一个找到的值， 如果为 null 则返回 “”
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的字符串类型
     */
    public static String findValueAsText(String jsonData, String fieldName) {
        return findNode(jsonData, fieldName).asText("");
    }

    /**
     * 根据属性名获取该属性的所有值
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的字符串类型
     */
    public static List<String> findValuesAsText(String jsonData, String fieldName) {
        return findNode(jsonData).findValuesAsText(fieldName);
    }

    /**
     * 根据属性名获取该属性的值，如果有多个则返回 第一个找到的值
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的int类型
     */
    public static int findValueAsInt(String jsonData, String fieldName) {
        return findNode(jsonData, fieldName).asInt();
    }

    /**
     * 根据属性名获取该属性的值，如果有多个则返回 第一个找到的值
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的long类型
     */
    public static long findValueAsLong(String jsonData, String fieldName) {
        return findNode(jsonData, fieldName).asLong();
    }

    /**
     * 根据属性名获取该属性的值，如果有多个则返回 第一个找到的值
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的double类型
     */
    public static double findValueAsDouble(String jsonData, String fieldName) {
        return findNode(jsonData, fieldName).asDouble();
    }

    /**
     * 根据属性名获取该属性的值，如果有多个则返回 第一个找到的值
     *
     * @param jsonData  json串
     * @param fieldName 属性名
     * @return fieldName 对象的值的boolean类型
     */
    public static boolean findValueAsBoolean(String jsonData, String fieldName) {
        return findNode(jsonData, fieldName).asBoolean();
    }

    public static JsonNode findNode(String jsonData) {
        try {
            return OBJECT_MAPPER.readTree(jsonData);
        } catch (IOException e) {
            throw new IllegalArgumentException("Json data is illegitmacy.");
        }
    }

    public static JsonNode findNode(String jsonData, String fieldName) {
        try {
            return OBJECT_MAPPER.readTree(jsonData).findValue(fieldName);
        } catch (IOException e) {
            throw new IllegalArgumentException("Json data is illegitmacy.");
        }
    }

    public static List<JsonNode> findNodes(String jsonData, String fieldName) {
        try {
            return OBJECT_MAPPER.readTree(jsonData).findValues(fieldName);
        } catch (IOException e) {
            throw new IllegalArgumentException("Json data is illegitmacy.");
        }
    }

    public static boolean isJsonString(String jsonData) {
        try {
            OBJECT_MAPPER.readTree(jsonData);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static <T> T readValue(byte[] source, Class<T> clazz) {
        if (Objects.nonNull(source) && source.length > 0) {
            try {
                return OBJECT_MAPPER.readValue(source, clazz);
            } catch (IOException ex) {
                throw new IllegalArgumentException("[data read fail] exception : " + ex.getMessage(), ex);
            }
        }
        return null;
    }


    public static <T> T readValue(String source, Class<T> clazz) {
        if (StrUtil.isNotBlank(source)) {
            try {
                return OBJECT_MAPPER.readValue(source, clazz);
            } catch (IOException ex) {
                throw new IllegalArgumentException("[data read fail] exception : " + ex.getMessage(), ex);
            }
        }
        return null;
    }

}
